#!/usr/bin/perl
###############################################################################
#                                                                             #
# IPFire.org - A linux based firewall                                         #
# Copyright (C) 2007-2022  IPFire Team  <info@ipfire.org>                     #
#                                                                             #
# This program is free software: you can redistribute it and/or modify        #
# it under the terms of the GNU General Public License as published by        #
# the Free Software Foundation, either version 3 of the License, or           #
# (at your option) any later version.                                         #
#                                                                             #
# This program is distributed in the hope that it will be useful,             #
# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
# GNU General Public License for more details.                                #
#                                                                             #
# You should have received a copy of the GNU General Public License           #
# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
#                                                                             #
###############################################################################

use strict;

require '/var/ipfire/general-functions.pl';
require '/var/ipfire/connscheduler/lib.pl';

# seems to be necessary
my $sleep_after_profile = 5;

my ($second, $minute, $hour, $day, $month ,$year, $weekday) = localtime(time);
# correction for weekday, I am used to weeks starting with Monday (= 0) ;-)
$weekday = ($weekday + 6) % 7;
# get the closest thing possible
$minute = int($minute / 5) * 5;


if ( $ARGV[0] eq 'hangup' )
{
  &hangup();
}
elsif ( $ARGV[0] eq 'dial' )
{
  &dial();
}
elsif ( $ARGV[0] eq 'reconnect' )
{
  &reconnect();
}
elsif ( $ARGV[0] eq 'profile' )
{
  &profile($ARGV[1]);
}
elsif ( $ARGV[0] eq 'timer' )
{
  &timer();
}
elsif ( $ARGV[0] eq 'test' )
{
  &test();
}
else
{
  print "Usage: $0 {dial | hangup | reconnect | profile nr# }\n";
}

exit 0;


#   __                  _   _
#  / _|                | | (_)
# | |_ _   _ _ __   ___| |_ _  ___  _ __  ___
# |  _| | | | '_ \ / __| __| |/ _ \| '_ \/ __|
# | | | |_| | | | | (__| |_| | (_) | | | \__ \
# |_|  \__,_|_| |_|\___|\__|_|\___/|_| |_|___/
#
sub hangup
{
  # Kill connectd if running to prevent redial
  system('/bin/killall', 'connectd');

  unless ( -e "${General::swroot}/red/active" )
  {
    &General::log("ConnSched already disconnected");
    return;
  }

  &General::log("ConnSched disconnect");
  unless ( system('/etc/rc.d/init.d/network', 'stop', 'red') == 0 )
  {
    &General::log("ConnSched disconnect failed: $?");
    return;
  }

  # now wait for active triggerfile and ppp daemon to disappear
  # wait maximum 60 seconds
  my $counter = 60;
  sleep 1;
  while ( -e "${General::swroot}/red/active" || -e '/var/run/ppp-ipfire.pid' || $counter == 0 )
  {
    sleep 1;
    $counter--;
  }
}


sub dial
{
  if ( -e "${General::swroot}/red/active" )
  {
    &General::log("ConnSched already connected");
    return;
  }

  &General::log("ConnSched connect");
  unless ( system('/etc/rc.d/init.d/network', 'start', 'red') == 0 )
  {
    &General::log("ConnSched connect failed: $?");
    return;
  }

  # wait maximum 60 seconds for active triggerfile
  my $counter = 60;
  until ( -e "${General::swroot}/red/active" || $counter == 0 )
  {
    sleep 1;
    $counter--;
  }
}


sub reconnect
{
  &hangup() if ( -e "${General::swroot}/red/active" );
  # now wait for active triggerfile and ppp daemon to disappear
  # wait maximum 60 seconds
  my $counter = 60;
  sleep 1;
  while ( -e "${General::swroot}/red/active" || -e '/var/run/ppp-ipfire.pid' || $counter == 0 )
  {
    sleep 1;
    $counter--;
  }
  &dial();
}


sub profile
{
  my $profile = shift;
  my $restart_red = 0;

  unless ( ($profile > 0) and ($profile < $CONNSCHED::maxprofiles) )
  {
    &General::log("ConnSched invalid profile: $profile");
    return;
  }

  unless ( -e "${General::swroot}/ppp/settings-$profile" )
  {
    &General::log("ConnSched profile file does not exist: $profile");
    return;
  }

  if ( -e "${General::swroot}/red/active" )
  {
    # remember to restart red after changing profile
    $restart_red = 1;
    &hangup();
  }

  &General::log("ConnSched select profile: $profile");

  # Method to change Profile from pppsetup.cgi
	unlink("${General::swroot}/ppp/settings");
	link("${General::swroot}/ppp/settings-$profile", "${General::swroot}/ppp/settings");
	system ("/usr/bin/touch", "${General::swroot}/ppp/updatesettings");

  if ( $restart_red == 1 )
  {
    ## FIXME: do we need to do this ?
    sleep($sleep_after_profile);
    &dial();
  }
}


# fcronjob entry
sub timer
{
  for my $i ( 0 .. $#CONNSCHED::config )
  {
    next if ( $CONNSCHED::config[$i]{'ACTIVE'} ne 'on' );

    my $action_hour = substr($CONNSCHED::config[$i]{'TIME'},0,2);
    my $action_minute = substr($CONNSCHED::config[$i]{'TIME'},3,2);

    next if ( $action_hour != $hour );
    next if ( $action_minute != $minute );

    if ( $CONNSCHED::config[$i]{'DAYSTYPE'} eq 'days' )
    {
      my @temp = split(/-/,$CONNSCHED::config[$i]{'DAYS'},2);

      my $daystart = substr($temp[0], 0, -1);
      my $dayend = substr($temp[1], 1);

      next if ( ($day < $daystart) || ($day > $dayend) );
    }
    else
    {
      next if ( index($CONNSCHED::config[$i]{'WEEKDAYS'}, $CONNSCHED::weekdays[$weekday]) == -1 );
    }


    if ( $CONNSCHED::config[$i]{'ACTION'} eq 'reconnect' )
    {
      &reconnect()
    }
    elsif ( $CONNSCHED::config[$i]{'ACTION'} eq 'dial' )
    {
      &dial();
    }
    elsif ( $CONNSCHED::config[$i]{'ACTION'} eq 'hangup' )
    {
      &hangup();
    }
    elsif ( $CONNSCHED::config[$i]{'ACTION'} eq 'select profile' )
    {
      &profile($CONNSCHED::config[$i]{'PROFILENR'});
    }
    elsif ( $CONNSCHED::config[$i]{'ACTION'} eq 'reboot' )
    {
      &General::log("ConnSched reboot");
    	system ("/usr/local/bin/ipfirereboot", "boot");
    }
    elsif ( $CONNSCHED::config[$i]{'ACTION'} eq 'shutdown' )
    {
      &General::log("ConnSched shutdown");
    	system ("/usr/local/bin/ipfirereboot", "down");
    }
    elsif ( $CONNSCHED::config[$i]{'ACTION'} eq 'ipsecstart' )
    {
      &General::log("ConnSched ipsecstart");
       system ("/usr/local/bin/ipsecctrl", "S");
    }
    elsif ( $CONNSCHED::config[$i]{'ACTION'} eq 'ipsecstop' )
    {
      &General::log("ConnSched ipsecstop");
       system ("/usr/local/bin/ipsecctrl", "D");
    }
    else
    {
      # okay ? an event we don't know about
    }
  }
}
